package com.youyou.lambdaTest;
import com.youyou.entity.User;
import org.junit.Before;
import org.junit.Test;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeSet;
import java.util.stream.Collectors;


/**
 * @author:Lau
 * @create: 2023-01-31 09:56
 * @Description:
 */

public class Lambda {

    List<User> list = new ArrayList<>();

    @Before
    public void init() {
        list.add(new User(1, "小a", "男", 32, BigDecimal.valueOf(1600)));
        list.add(new User(2, "小b", "男", 30, BigDecimal.valueOf(1800)));
        list.add(new User(3, "小c", "女", 20, BigDecimal.valueOf(1700)));
        list.add(new User(4, "小d", "男", 38, BigDecimal.valueOf(1500)));
        list.add(new User(5, "小e", "女", 25, BigDecimal.valueOf(1200)));
        System.out.println("--------------------原始数据--------------------");
        list.forEach(System.out::println);
        System.out.println("--------------------原始数据--------------------");
    }

    @Test
    public void removeIf() {
        //list过滤
        list.removeIf(o -> "小a".equals(o.getUserName()));
        System.out.println("-------------removeIf---------start--------");
        list.forEach(System.out::println);
        System.out.println("-------------removeIf----------end-------");
    }

    @Test
    public void filter() {
        List<User> collect = list.stream().filter(o -> o.getUserId() != null && o.getUserId() == 1)
                .collect(Collectors.toList());
        System.out.println("-------------filter---------start--------");
        collect.forEach(System.out::println);
        System.out.println("-------------filter----------end-------");

    }

    /**
     * 注意： 建议使用主键id查找，findFirst() 和 findAny() 都是获取列表中的第一条数据，但是findAny()操作，返回的元素是不确定的，
     * 对于同一个列表多次调用findAny()有可能会返回不同的值。使用findAny()是为了更高效的性能。如果是数据较少，串行地情况下，
     * 一般会返回第一个结果，如果是并行（parallelStream并行流）的情况，那就不能确保是第一个。
     */
    @Test
    public void selectOne() {
        User user = list.stream().findFirst().orElse(null);
        System.out.println(user);
        User user1 = list.stream().filter(o -> o.getUserId() != null && o.getUserId() == 5).findAny().orElse(null);
        System.out.println(user1);
    }

    /**
     * 使用 map() 将流中的每一个元素 T 映射为 R（类似类型转换）
     */
    @Test
    public void map() {
        List<Integer> collect = list.stream().map(User::getUserId).collect(Collectors.toList());
        System.out.println(collect);
    }

    /**
     * 使用 flatMap() 将流中的每一个元素 T 映射为一个流，再把每一个流连接成为一个流
     * 多重集合 嵌套时有妙用
     */
    @Test
    public void flatMap() {
        //创建城市
        List<List<String>> cityList = new ArrayList<>();
        List<String> cityList1 = new ArrayList<String>();
        cityList1.add("福州");
        cityList1.add("宁德");
        List<String> cityList2 = new ArrayList<String>();
        cityList2.add("深圳");
        cityList2.add("广州");
        cityList.add(cityList1);
        cityList.add(cityList2);

        List<String> collect = cityList.stream().flatMap(it -> it.stream()).collect(Collectors.toList());
        System.out.println(collect);

    }

    /**
     * 去除重复的数据
     */
    @Test
    public void distinct() {
        List<User> userList = new ArrayList<User>();
        userList.add(new User(1, "小a", "男", 32, BigDecimal.valueOf(1600)));
        userList.add(new User(2, "小b", "男", 30, BigDecimal.valueOf(1800)));
        userList.add(new User(3, "小c", "女", 20, BigDecimal.valueOf(1700)));
        userList.add(new User(4, "小d", "男", 38, BigDecimal.valueOf(1500)));
        userList.add(new User(5, "小e", "女", 25, BigDecimal.valueOf(1200)));
        List<String> cityList = new ArrayList<String>();
        cityList.add("福州");
        cityList.add("宁德");
        cityList.add("宁德");
        List<String> collect = cityList.stream().distinct().collect(Collectors.toList());
        System.out.println(collect);

        // 根据对象的某个属性值  或 多个
        List<User> cbList = userList.stream().collect(Collectors.collectingAndThen(
                Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(tc -> tc.getSex()))), ArrayList::new));
        System.out.println(cbList);

    }

    /**
     * limit(long n) 方法用于返回前n条数据，skip(long n) 方法用于跳过前n条数据
     */
    @Test
    public void limit() {
        //List<User> collect = list.stream().limit(1).collect(Collectors.toList());
        List<User> collect = list.stream().skip(1).collect(Collectors.toList());
        System.out.println(collect);
    }

    /**
     * 使用 anyMatch(T -> boolean) 判断流中是否有一个元素匹配给定的 T -> boolean 条件。
     * 使用 allMatch(T -> boolean) 判断流中是否所有元素都匹配给定的 T -> boolean 条件。
     * 使用 noneMatch(T -> boolean) 流中是否没有元素匹配给定的 T -> boolean 条件。
     */
    @Test
    public void anyMatch() {
        boolean a = list.stream().anyMatch(o -> o.getUserId().equals(1));
        System.out.println(a);
        boolean b = list.stream().allMatch(o -> o.getUserId().equals(1));
        System.out.println(b);
        boolean c = list.stream().filter(o -> o != null && o.getUserId() != null)
                .noneMatch(o -> o.getUserId().equals(10));
        System.out.println(c);

    }

    /**
     * reduce 用于组合流中的元素，如求和，求积，求最大值等
     */
    @Test
    public void reduce1() {
        //用户列表中年龄的最大值、最小值、总和 注意空值处理
        int maxVal = list.stream().map(User::getAge).filter(Objects::nonNull).reduce(Integer::max).get();
        int minVal = list.stream().map(User::getAge).filter(Objects::nonNull).reduce(Integer::min).get();
        int sumVal = list.stream().map(User::getAge).filter(Objects::nonNull).reduce(0, Integer::sum);
        System.out.println(maxVal);
        System.out.println(minVal);
        System.out.println(sumVal);
    }

    /**
     * reduce是一个终结操作，它能够通过某一个方法，对元素进行削减操作。
     * 该操作的结果会放在一个Optional变量里返回。
     * 可以利用它来实现很多聚合方法比如count,max,min等。
     * T reduce(T identity, BinaryOperator accumulator);
     * 第一个参数是我们给出的初值，第二个参数是累加器，可以自己用实现接口完 成想要的操作，
     * 这里使用Bigdecimal的add方法 最后reduce会返回计算后的结果
     */
    @Test
    public void reduce2() {
        BigDecimal result2 = list.stream().map(User::getSalary)
                .filter(Objects::nonNull).reduce(BigDecimal.ZERO,BigDecimal::add);
        System.out.println(result2);
    }

    /**
     * 关键概念：初始值的定义（Identity)，累加器（Accumulator)，组合器（Combiner）
     *
     * Identity : 定义一个元素代表是归并操作的初始值，如果Stream 是空的，也是Stream 的默认结果
     * Accumulator: 定义一个带两个参数的函数，第一个参数是上个归并函数的返回值，第二个是Strem 中下一个元素。
     * Combiner: 调用一个函数来组合归并操作的结果，当归并是并行执行或者当累加器的函数和累加器的实现类型不匹配时才会调用此函数。
     * 也就是说""就是我们的初始值，(a,b)->a+b就是我们的累加器，其中a就是上一次的计算结果，b就是Stream流中当前元素，
     * 而后面的a+b则是计算规则，比如如果我们改成a*b，那就是计算乘积了，当然我们也可以用方法引用来代替 lambda 表达式。
     * String s = "Who Love Solo";
     * double res = Arrays.stream(s).mapToDouble(a ->a.length()).reduce(0,Double::sum);
     */
    @Test
    public void reduce3() {
        List<String> letters = Arrays.asList("a", "b", "c", "d", "e");
        String result = letters.stream().reduce("", (a, b) -> a + b);
        System.out.println(result);
    }

    /**
     * 使用 count() 可以对列表数据进行统计
     */
    @Test
    public void count() {
        long count = list.stream().filter(o -> {
            String gender = Optional.ofNullable(o).map(User::getSex).orElse("");
            return "男".equals(gender);
        }).count();
        System.out.println(count);

    }

    /**
     * IntSummaryStatistics类提供了用于计算的平均值、总数、最大值、最小值、总和等方法
     */
    @Test
    public void IntSummaryStatistics() {
        //获取IntSummaryStatistics对象
        IntSummaryStatistics ageStatistics = list.stream().collect(Collectors.summarizingInt(User::getAge));
        //统计：最大值、最小值、总和、平均值、总数
        System.out.println("最大年龄：" + ageStatistics.getMax());
        System.out.println("最小年龄：" + ageStatistics.getMin());
        System.out.println("年龄总和：" + ageStatistics.getSum());
        System.out.println("平均年龄：" + ageStatistics.getAverage());
        System.out.println("员工总数：" + ageStatistics.getCount());

        List<String> letters = Arrays.asList("a", "b", "c", "d", "e");
        String result = letters
                .stream()
                .reduce("", (a, b) -> a + b);
        System.out.println(result);
    }


}

